package com.empire.module.sporttery.service.footballmatch;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.empire.module.sporttery.controller.app.football.vo.*;
import com.empire.module.sporttery.convert.footballmatch.FootballMatchConvert;
import com.empire.module.sporttery.util.SportteryUtils;
import com.empire.module.sporttery.vo.*;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import org.springframework.validation.annotation.Validated;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.*;


import com.empire.module.sporttery.controller.admin.footballmatch.vo.*;
import com.empire.module.sporttery.dal.dataobject.footballmatch.FootballMatchDO;
import com.empire.framework.common.pojo.PageResult;
import com.empire.framework.common.util.object.BeanUtils;

import com.empire.module.sporttery.dal.mysql.footballmatch.FootballMatchMapper;

import static com.empire.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.empire.module.sporttery.enums.ErrorCodeConstants.*;

/**
 * 竞猜足彩比赛 Service 实现类
 *
 * @author 无敌仙人
 */
@Service
@Validated
public class FootballMatchServiceImpl implements FootballMatchService {
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private FootballMatchMapper footballMatchMapper;

    @Override
    public Integer createFootballMatch(FootballMatchSaveReqVO createReqVO) {
        // 插入
        FootballMatchDO footballMatch = BeanUtils.toBean(createReqVO, FootballMatchDO.class);
        footballMatchMapper.insert(footballMatch);
        // 返回
        return footballMatch.getId();
    }

    @Override
    public void updateFootballMatch(FootballMatchSaveReqVO updateReqVO) {
        // 校验存在
        validateFootballMatchExists(updateReqVO.getId());
        // 更新
        FootballMatchDO updateObj = BeanUtils.toBean(updateReqVO, FootballMatchDO.class);
        footballMatchMapper.updateById(updateObj);
    }

    @Override
    public void deleteFootballMatch(Integer id) {
        // 校验存在
        validateFootballMatchExists(id);
        // 删除
        footballMatchMapper.deleteById(id);
    }

    private void validateFootballMatchExists(Integer id) {
        if (footballMatchMapper.selectById(id) == null) {
            throw exception(FOOTBALL_MATCH_NOT_EXISTS);
        }
    }

    @Override
    public FootballMatchDO getFootballMatch(Integer id) {
        return footballMatchMapper.selectById(id);
    }

    @Override
    public PageResult<FootballMatchDO> getFootballMatchPage(FootballMatchPageReqVO pageReqVO) {
        return footballMatchMapper.selectPage(pageReqVO);
    }

    @Override
    public PageResult<FootballMatchDO> getFootballMatchPage(AppFootballMatchPageReqVO pageReqVO) {
        FootballMatchPageReqVO pvo = BeanUtils.toBean(pageReqVO, FootballMatchPageReqVO.class);
        return footballMatchMapper.selectPage(pvo);
    }

    @Override
    public List<FootballMatchDO> getFootballMatchByDay(AppFootballMatchPageReqVO pageReqVO) {
        return footballMatchMapper.selectLike(pageReqVO);
    }

    @Override
    public List<FootballDayResultVO> getDayResults(AppFootballMatchPageReqVO pageReqVO) {
        //获取查询时间列表
        List<String> days = SportteryUtils.getFootballStartTime(pageReqVO.getPageNo(), pageReqVO.getPageSize(), pageReqVO.getSelectedDate());
        //根据strat_time开始查询
        List<FootballMatchDO> list = footballMatchMapper.selectList(new LambdaQueryWrapper<FootballMatchDO>()
                .in(true, FootballMatchDO::getStartTime, days).orderByAsc(FootballMatchDO::getOpenTime));
        //根据start_time分组
        Map<String, List<FootballMatchDO>> groupedByDay = list.stream()
                .collect(Collectors.groupingBy(FootballMatchDO::getStartTime));
        List<FootballDayResultVO> rs = new ArrayList<>();
        //根据每一天的比赛，进行分析统计
        for (String k : groupedByDay.keySet()) {
            FootballDayResultVO ds = new FootballDayResultVO();
            ds.setStartTime(k);
            List<FootballMatchDO> footballMatchList = groupedByDay.get(k);
            setShootAndSelectCount(footballMatchList, ds);
            ds.setMatchCount(footballMatchList.size());
            ds.seMatchDoList(footballMatchList);
            rs.add(ds);
        }
        //排序一下，方便前端显示,
        return rs.stream()
                .sorted(Comparator.comparing(FootballDayResultVO::getDayTime))
                .collect(Collectors.toList());
    }

    /**
     * 设置选择场次和计算命中场次
     * @param footballMatchList 天-对应的赛事列
     * @param ds 结果集
     */
    private static void setShootAndSelectCount(List<FootballMatchDO> footballMatchList, FootballDayResultVO ds) {
        int selectCount = 0;
        int shootCount = 0;
        for (FootballMatchDO footballMatchDO : footballMatchList) {
            boolean hasShoot = false;
            boolean hasSelect = false;
            //选胜
            String winPay = footballMatchDO.getWinPay();
            Integer winFlag = footballMatchDO.getWinFlag();
            Integer winSelect = footballMatchDO.getWinSelect();
            if (StrUtil.isNotEmpty(winPay) && winSelect == 1) {
                selectCount++;
                hasSelect = true;
            }
            if (StrUtil.isNotEmpty(winPay) && winFlag == 1 && winFlag.equals(winSelect)) {
                shootCount++;
                hasShoot = true;
            }

            Integer drawWinFlag = footballMatchDO.getDrawWinFlag();
            Integer drawWinSelect = footballMatchDO.getDrawWinSelect();
            if (StrUtil.isNotEmpty(winPay) && !hasSelect && drawWinSelect == 1) {
                selectCount++;
                hasSelect = true;
            }
            if (StrUtil.isNotEmpty(winPay) && !hasShoot && drawWinFlag == 1 && drawWinFlag.equals(drawWinSelect)) {
                shootCount++;
                hasShoot = true;
            }

            Integer visitWinFlag = footballMatchDO.getVisitWinFlag();
            Integer visitWinSelect = footballMatchDO.getVisitWinSelect();
            if (StrUtil.isNotEmpty(winPay) && !hasSelect && visitWinSelect == 1) {
                selectCount++;
                hasSelect = true;
            }
            if (StrUtil.isNotEmpty(winPay) && !hasShoot && visitWinFlag == 1 && visitWinFlag.equals(visitWinSelect)) {
                shootCount++;
                hasShoot = true;
            }

            Integer letWinFlag = footballMatchDO.getLetWinFlag();
            Integer letWinSelect = footballMatchDO.getLetWinSelect();
            if (!hasSelect && letWinSelect == 1) {
                selectCount++;
                hasSelect = true;
            }
            if (!hasShoot && letWinFlag == 1 && letWinFlag.equals(letWinSelect)) {
                shootCount++;
                hasShoot = true;
            }

            Integer letDrawWinFlag = footballMatchDO.getLetDrawWinFlag();
            Integer letDrawWinSelect = footballMatchDO.getLetDrawWinSelect();
            if (!hasSelect && letDrawWinSelect == 1) {
                selectCount++;
                hasSelect = true;
            }
            if (!hasShoot && letDrawWinFlag == 1 && letDrawWinFlag.equals(letDrawWinSelect)) {
                shootCount++;
                hasShoot = true;
            }

            Integer letVisitWinFlag = footballMatchDO.getLetVisitWinFlag();
            Integer letVisitWinSelect = footballMatchDO.getLetVisitWinSelect();
            if (!hasSelect && letVisitWinSelect == 1) {
                selectCount++;
            }
            if (!hasShoot && letVisitWinFlag == 1 && letVisitWinFlag.equals(letVisitWinSelect)) {
                shootCount++;
            }
            //设置半全场/比分/进球数赔率
            String halfWholeOddList = footballMatchDO.getHalfWholeOddList();
            
        }
        ds.setSelectCount(selectCount);
        ds.setShoot(shootCount);
    }

    @Override
    public List<FootballMatchDO> getMatchs() {
        //获取查询时间列表，默认是充今天开始
        List<String> days = SportteryUtils.getFootballStartTime(1, 7, "2025-02-22");
        //根据strat_time开始查询
        return footballMatchMapper.selectList(new LambdaQueryWrapper<FootballMatchDO>()
                .in(true, FootballMatchDO::getStartTime, days).orderByAsc(FootballMatchDO::getOpenTime));
    }

    @Override
    public AppFootballBuySchemeRespVO calculateBonus(AppFootballCalculateBonusReqVO vo,List<MatchBonusItemVO> list) {
        //获取最新赔率及比赛信息
        List<MatchBonusVO> select_matchs = new ArrayList<>();
        for(CalculateBonusMatchReqVO v:vo.getMatchs()){
            List<MatchBonusItemVO> ms = list.stream().filter(x->x.getMatchId().equals(v.getMatchId())).toList();
            MatchBonusItemVO info = ms.getFirst();

            MatchBonusVO mv = new MatchBonusVO();

            mv.setMatchInfo(info);
            mv.setMatchId(info.getMatchId());

            List<BonusVO> bvs = info.getBonusList().stream().filter(x->x.getC().equals(v.getC())).toList();
            BonusVO bv = bvs.getFirst();
            mv.setBonus(bv);
            mv.setGameCode(bv.getGc());

            select_matchs.add(mv);

        }

        //先判断过关
        List<GuoguanVO> guoguans = getGuoguan(select_matchs);

        if(!vo.getGuoGuans().isEmpty() && guoguans.size() == vo.getGuoGuans().size()){
            guoguans = vo.getGuoGuans();
        }
        AppFootballBuySchemeRespVO result =new AppFootballBuySchemeRespVO();
        if(!guoguans.isEmpty()){
            //计算奖金
            result = calculate(select_matchs,guoguans,vo.getNum());
            //将选择的选项，重新整理一下，显示到页面
            List<AppFootballMatchBonusRespVO> matchBonusList =new ArrayList<>();
            //按照场次进行分组
            Map<String, List<MatchBonusVO>> groupDatas= select_matchs.stream()
                    .collect(Collectors.groupingBy(MatchBonusVO::getMatchId));

            for(String key:groupDatas.keySet()){
                List<MatchBonusVO> blist = groupDatas.get(key);
                //按照游戏进行分组
                Map<String, List<MatchBonusVO>> groupByGame= blist.stream()
                        .collect(Collectors.groupingBy(MatchBonusVO::getGameCode));

                AppFootballMatchBonusRespVO mbrvo = new AppFootballMatchBonusRespVO();
                List<FootballGameBonusVO> gamelist=new ArrayList<>();
                for(String bkey:groupByGame.keySet()){
                    FootballGameBonusVO fb =new FootballGameBonusVO();
                    if(bkey.equals("spf")){
                        fb.setN("胜平负/让");
                        fb.setC("spf");
                    }
                    if(bkey.equals("rspf")){
                        fb.setN("胜平负/让");
                        fb.setC("spf");
                    }
                    if(bkey.equals("ttg")){
                        fb.setN("总进球");
                        fb.setC("ttg");
                    }
                    if(bkey.equals("crs")){
                        fb.setN("比分");
                        fb.setC("crs");
                    }
                    if(bkey.equals("bqc")){
                        fb.setN("半全场");
                        fb.setC("bqc");
                    }
                    List<AppMatchBonusVO> appmblist = FootballMatchConvert.INSTANCE.convertList4(groupByGame.get(bkey));
                    fb.setBonusList(appmblist);
                    gamelist.add(fb);
                }
                //将spf及rspf合并在一起显示,按照重新游戏类别进行分组
                Map<String, List<FootballGameBonusVO>> groupByGameCag= gamelist.stream()
                        .collect(Collectors.groupingBy(FootballGameBonusVO::getN));


                MatchBonusVO mm = blist.getFirst();
                mbrvo.setMatchId(mm.getMatchId());
                mbrvo.setMatchDate(mm.getMatchInfo().getMatchDate());
                mbrvo.setMatchDateStr(mm.getMatchInfo().getMatchDateStr());
                mbrvo.setMatchNumStr(mm.getMatchInfo().getMatchNumStr());
                mbrvo.setMatchTime(mm.getMatchInfo().getMatchTime());
                mbrvo.setAwayTeamAbbName(mm.getMatchInfo().getAwayTeamAbbName());
                mbrvo.setHomeTeamAbbName(mm.getMatchInfo().getHomeTeamAbbName());
                mbrvo.setLeagueAbbName(mm.getMatchInfo().getLeagueAbbName());

                mbrvo.setGameMap(groupByGameCag);
                matchBonusList.add(mbrvo);
            }
            result.setSelectNum(groupDatas.size());
            result.setSelectBonusList(matchBonusList);
        }

        return result;
    }

    /**
     *  1单关，2二串一，3三串一、4四串一、5二串一、6六串一、7七串一、8八串一
     *          //如果只选择一场比赛，就判断是否单关
     *         //如果选择两场以上的比赛，那么就开始判断串串
     *         //如果有半全场，总进球，比分，那么最多只能是4串1，其他的，最多8串1
     * @param matchs
     */
    private List<GuoguanVO> getGuoguan(List<MatchBonusVO> matchs){
        //判断是混合的，还是全部单关，只要一个不是单关，那么就认为是混合的，混合的只能2场以上，
        boolean ismix = false;
        boolean isdan = true;
        for(MatchBonusVO v : matchs){
            if(!v.getBonus().getS()){
                isdan = false;
                break;
            }
        }
        for(MatchBonusVO v : matchs){
            //只要存在半全场，比分，总进球，就是混合的
            if(Objects.equals(v.getBonus().getGc(), "bqc") || Objects.equals(v.getBonus().getGc(), "crs") || Objects.equals(v.getBonus().getGc(), "ttg")){
                ismix = true;
                break;
            }
        }

        //按照场次进行分组
        Map<String, List<MatchBonusVO>> groupDatas= matchs.stream()
                .collect(Collectors.groupingBy(MatchBonusVO::getMatchId));
        //判断有几场比赛
        int num = groupDatas.size();

        List<Integer> guans = new ArrayList<>();
        if(isdan){//是单关，就吧单关加上去
            guans.add(1);
        }

        int max_num = 8;
        if(ismix){
            max_num = Math.min(num, 4);
        }else{
            if(num<8){
                max_num = num;
            }
        }
        //根据场次生成串串
        for(int i=2;i<=max_num;i++){
            guans.add(i);
        }

        //转换成对象
        //将过关翻译，协助前端显示
        List<GuoguanVO> guoguanlist=new ArrayList<>();
        for(Integer g : guans){
            GuoguanVO guo=new GuoguanVO();
            if(g ==1){
                guo.setN("单关");
            }else {
                guo.setN(g+"串1");
            }
            guo.setNo(g);
            guo.setX(false);
            guoguanlist.add(guo);
        }
        //默认选择最后一个过关
        if(!guoguanlist.isEmpty()){
            guoguanlist.getLast().setX(true);
        }
        return guoguanlist;
    }

    /**
     * 奖金奖金计算
     * @param matchs
     */
    private AppFootballBuySchemeRespVO calculate(List<MatchBonusVO> matchs,List<GuoguanVO> guoguans,int bei){
        //按照场次进行分组
        Map<String, List<MatchBonusVO>> groupDatas= matchs.stream()
                .collect(Collectors.groupingBy(MatchBonusVO::getMatchId));

        List<List<MatchBonusVO>> all_combinations=new ArrayList<>();
        for(GuoguanVO g : guoguans){
            if(g.getX()){
                //组合串串
                all_combinations.addAll(getBonusCombination(groupDatas,g.getNo()));
            }

        }
        //计算奖金
        List<BuySchemeVO> schemeList=new ArrayList<>();
        for(List<MatchBonusVO> alist:all_combinations){
            BuySchemeVO sc=new BuySchemeVO();

            double jiang = bei*2;
            for(MatchBonusVO v:alist){
                jiang *= v.getBonus().getV();
            }
            double buy = 2 * bei;
            sc.setNum(1);
            sc.setBuyMoney(buy);
            sc.setBonusList(alist);
            sc.setTotalBonus(Math.round(jiang * 100.0) / 100.0);
            sc.setBeiNum(bei);

            schemeList.add(sc);
        }
        //找到最大奖金和最小奖金
        double min = schemeList.getFirst().getTotalBonus();
        double max = schemeList.getFirst().getTotalBonus();
        double totalMoney = 0;
        int totalNum = 0;
        for(BuySchemeVO v:schemeList){
            if(v.getTotalBonus()>max){
                max = v.getTotalBonus();
            }
            if(v.getTotalBonus()<min){
                min = v.getTotalBonus();
            }
            totalMoney +=v.getBuyMoney();
            totalNum +=v.getNum();
        }

        AppFootballBuySchemeRespVO result=new AppFootballBuySchemeRespVO();
        result.setGuoguans(guoguans);
        result.setBeiNum(bei);
        result.setMinBonus(min);
        result.setMaxBonus(max);
        result.setTotalNum(totalNum);
        result.setTotalMoney(totalMoney);
        result.setBuySchemeList(schemeList);

        return result;

    }

    public List<List<MatchBonusVO>> getBonusCombination(Map<String, List<MatchBonusVO>> groupDatas ,int guan){
        List<List<MatchBonusVO>> combinations=new ArrayList<>();
        //排列组合
        List<List<String>> key_combinations = combine(new ArrayList<>(groupDatas.keySet()), guan);
        key_combinations.forEach(xs->{
            List<List<MatchBonusVO>> blist=new ArrayList<>();
            xs.forEach(items->{
                blist.add(groupDatas.get(items));
            });
            combinations.addAll(getCombinationsIterative(blist));
        });
        return combinations;
    }



    /**
     * 过个列表进行排列组合
     * @param lists
     * @return
     * @param <T>
     */
    public static <T> List<List<T>> getCombinationsIterative(List<List<T>> lists) {
        List<List<T>> result = new ArrayList<>();
        if (lists.isEmpty()) {
            result.add(new ArrayList<>());
            return result;
        }

        // 初始化为第一个列表的元素
        for (T item : lists.get(0)) {
            List<T> combination = new ArrayList<>();
            combination.add(item);
            result.add(combination);
        }

        // 依次处理剩余的列表
        for (int i = 1; i < lists.size(); i++) {
            List<List<T>> newResult = new ArrayList<>();
            List<T> currentList = lists.get(i);

            for (List<T> combination : result) {
                for (T item : currentList) {
                    List<T> newCombination = new ArrayList<>(combination);
                    newCombination.add(item);
                    newResult.add(newCombination);
                }
            }

            result = newResult;
        }

        return result;
    }
    public List<List<String>> combine(List<String> list, int k) {
        List<List<String>> result = new ArrayList<>();
        backtrack(list, 0, k, new ArrayList<>(), result);
        return result;
    }

    private void backtrack(List<String> list, int start, int k,
                           List<String> temp, List<List<String>> result) {
        if (temp.size() == k) {
            result.add(new ArrayList<>(temp));
            return;
        }

        for (int i = start; i < list.size(); i++) {
            temp.add(list.get(i));
            backtrack(list, i + 1, k, temp, result);
            temp.removeLast();
        }
    }

    @Override
    public Boolean footballMatchSelect(Integer fid, Integer index) {
        LambdaQueryWrapper<FootballMatchDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(FootballMatchDO::getMatchId, fid);
        this.footballMatchMapper.selectList(queryWrapper).forEach(e -> {
            int select;
            switch (index) {
                case 1:
                    select = getSelect(e.getWinSelect());
                    e.setWinSelect(select);
                    break;
                case 2:
                    select = getSelect(e.getDrawWinSelect());
                    e.setDrawWinSelect(select);
                    break;
                case 3:
                    select = getSelect(e.getVisitWinSelect());
                    e.setVisitWinSelect(select);
                    break;
                case 4:
                    select = getSelect(e.getLetWinSelect());
                    e.setLetWinSelect(select);
                    break;
                case 5:
                    select = getSelect(e.getLetDrawWinSelect());
                    e.setLetDrawWinSelect(select);
                    break;
                case 6:
                    select = getSelect(e.getLetVisitWinSelect());
                    e.setLetVisitWinSelect(select);
                    break;
                default:
                    break;
            }
            this.footballMatchMapper.updateById(e);
        });
        return true;
    }

    private static int getSelect(int select) {
        return select == 0 ? 1 : 0;
    }

}